package alert

import (
	"fmt"
	db "hulk-api/databases"
	"encoding/json"
	"github.com/garyburd/redigo/redis"
	"hulk-api/conf"
	"strconv"
	"time"
	"log"
	"strings"
	"github.com/prometheus/client_golang/api"
	"github.com/prometheus/client_golang/api/prometheus/v1"
	"context"
	"github.com/prometheus/common/model"
)

type MonitorAlertEntry struct {
	Ip string				`json:"ip"`
	Label string			`json:"label"`
	Type string				`json:"type"`
	Operation []string 		`json:"operation"`
	Expr []MonitorAlertExpr	`json:"expr"`
	Receiver string			`json:"receiver"`

	Title string			`json:"title"`
	AlertTime string		`json:"alerttime"`

	Status bool				`json:"status"` //报警状态，用于设置恢复报警
}


type MonitorAlertExpr struct {
	Key string				`json:"key"`
	Logic string			`json:"logic"`
	Value string			`json:"value"`
}

func GetAlertStatus(host string)(alerts []MonitorAlertEntry)  {
	redisconn := db.RedisPool.Get()
	defer redisconn.Close()

	triggerKey,_  :=  conf.Cfg.GetValue("redis","trigger_key")
	alertEntriesKeys, err := redis.Strings(redisconn.Do("HKEYS", triggerKey))

	if err != nil {
		log.Println("redis HGET error:", err)
		return
	}
	fmt.Println(len(alertEntriesKeys))

	for _,entry := range alertEntriesKeys {
		if strings.Split(entry,"-")[0] ==host {
			fmt.Println(entry)
					result,err := redis.String(redisconn.Do("HGET",triggerKey,entry))
					if err != nil {
						log.Println(err)
					}else {
						var e MonitorAlertEntry
						json.Unmarshal([]byte(result),&e)
						alerts = append(alerts,e)
					}
		}
	}



	return
}


func (entry *MonitorAlertEntry) Setting()   {
	fmt.Println("begin to setting ")
	fmt.Printf("%+v\n",entry)
	//entry.Status = true  //测试恢复
	go setAlert(entry.alertKey(),entry)  //设置告警
}



func (entry *MonitorAlertEntry) alertKey() string  {
	return entry.Ip + "-" +  entry.Label
}

func setAlert(k string , v interface{})  {
	redisconn := db.RedisPool.Get()
	defer redisconn.Close()

	triggerKey,_  :=  conf.Cfg.GetValue("redis","trigger_key")

	switch v.(type) {
	case *MonitorAlertEntry:

		if b,err := json.Marshal(v); err == nil {
			_,err =redisconn.Do("HSET",triggerKey,k,string(b))
			if err != nil {
				fmt.Println("设置触发器错误..." , err.Error())
			}
		} else {
			fmt.Println("设置报警项错误，id:" ,err.Error())
		}

	}
}

func alarm()  {
	alertEntries := redisHget()
	for _,entry := range alertEntries {
		var e MonitorAlertEntry
		json.Unmarshal([]byte(entry),&e)
		go e.monitoring() //TODO:会不会因为开启携程过多而消耗过多资源？

	}
}

func (e *MonitorAlertEntry) monitoring()  {
	trigger,ok := e.newMultiTriggerEntry().judge()
	if ok {
		//e.Title = "TCP连接状态异常"
		e.AlertTime  = time.Now().Format("2006/1/2 15:04:05")
		//fmt.Println("ojbk we will send dingding message")
		fmt.Printf("begin send dingding message %+v\n" , e)
		go e.sendDingDing(trigger)   //默认发送钉钉信息，其它方式，后续集成
		//go e.SendDingDingtest()
		//e.Status = true
		if !e.Status{
			k := e.Ip + "-" +  e.Label
			e.Status = true
			go setAlert(k,e)  //设置alert状态，
		}

	}else {
		if e.Status {
			fmt.Println("begin recover 报警")
			e.Title = e.Title + " 恢复"
			e.AlertTime  = time.Now().Format("2006/1/2 15:04:05")
			//fmt.Println("ojbk we will send dingding message")
			fmt.Printf("begin send dingding message %+v\n" , e)
			go e.sendDingDing(trigger)   //默认只发送钉钉信息，而且端是根据接受组来发送，而不是根据告警方式
			k := e.Ip + "-" +  e.Label
			e.Status = false
			go setAlert(k,e)  //设置alert状态，

		}
	}
}

type prometheusQuery struct {
	query string
	result float64
}

func (p *prometheusQuery) Query() *prometheusQuery {
	prometheusServer,_ := conf.Cfg.GetValue("prometheus","prometheus_server")
	cfg := api.Config{
		Address: prometheusServer,
	}
	proCli,err := api.NewClient(cfg)
	if err != nil {
		log.Println("connect to err is" , err.Error())
	}
	proApi := v1.NewAPI(proCli)
	ctx := context.Background()

	res,err := proApi.Query(ctx,p.query,time.Time{})
	m,ok := res.(model.Vector)
	if ok && m.Len() > 0 {
		p.result = float64(m[0].Value)
	}
	return p
}


type triggerEntry struct {
	currentStatus prometheusQuery
	logic string
	specify float64
}


func (t *triggerEntry)judge() bool  {
	switch t.logic {
	case ">":
		return t.currentStatus.Query().result > t.specify
	case ">=":
		return t.currentStatus.Query().result >= t.specify
	case "<":
		return t.currentStatus.Query().result < t.specify
	case "<=":
		return t.currentStatus.Query().result <= t.specify
	case "!=":
		return t.currentStatus.Query().result != t.specify
	default:
		log.Println("unknow logic")
		return false
	}

}


type multiTriggerEntry struct {
	logicSlice []string
	triggers []triggerEntry
}

func (m multiTriggerEntry) judge() (triggerEntry,bool)  {


	if len(m.triggers) == 1 {
		return m.triggers[0],m.triggers[0].judge()
	}

	responseTriggerEntry := m.triggers[0]
	flag := m.triggers[0].judge()

	for index := 1 ; index < len(m.triggers) ; index++ {
		j := index - 1
		switch strings.ToLower(m.logicSlice[j]) {   //尽量减少请求
		case "and":
			if flag {
				if m.triggers[index].judge() {
					responseTriggerEntry = m.triggers[index]
					flag = true
				} else {
					return m.triggers[index],false
				}
			}else {
				return responseTriggerEntry,false
			}
		case "or":
			if flag {
				return responseTriggerEntry,true
			}else {
				if m.triggers[index].judge() {
					return m.triggers[index],true
				}else {
					responseTriggerEntry = m.triggers[index]
					flag = false
				}
			}
		}
	}
	return responseTriggerEntry,flag
}


func (e *MonitorAlertEntry) newMultiTriggerEntry() (triggerentrys multiTriggerEntry) {
	switch e.Label {
	case "SystemAvgLoad":
		e.Title = "系统负载异常"
		var entrys []triggerEntry
		for _,entry :=range e.Expr {
			entrys = append(entrys,triggerEntry{
				currentStatus: prometheusQuery{
					query: fmt.Sprintf("%s{instance=~'%s.*'}",entry.Key,e.Ip),
				},
				logic: entry.Logic,
				specify: func(s string)float64 {
					res,err := strconv.ParseFloat(s,64)
					if err != nil {
						log.Println(err.Error(),"是否因为带单位了？")
					}
					return res
				}(entry.Value),

			})
		}
		triggerentrys.triggers = entrys
		triggerentrys.logicSlice = e.Operation
		return

	case "MemoryUsage":
		e.Title = "内存使用率过高"
		var entrys []triggerEntry
		for _,entry :=range e.Expr {
			entrys = append(entrys,triggerEntry{
				currentStatus: prometheusQuery{
					query: fmt.Sprintf("1 - (node_memory_MemAvailable_bytes{instance=~'%s.*'}) / (node_memory_MemTotal_bytes {instance=~'%s.*'})",e.Ip,e.Ip),
				},
				logic: entry.Logic,
				specify: func(s string)float64 {
					res,err := strconv.ParseFloat(s,64)
					if err != nil {
						log.Println(err.Error(),"是否因为带单位了？")
					}
					return res
				}(entry.Value),

			})
		}
		triggerentrys.triggers = entrys
		triggerentrys.logicSlice = e.Operation
		return
	case "RootPartUsage":
		e.Title = "根分区使用率过高"
		var entrys []triggerEntry
		for _,entry :=range e.Expr {
			entrys = append(entrys,triggerEntry{
				currentStatus: prometheusQuery{
					query: fmt.Sprintf("1- ((node_filesystem_avail_bytes{instance=~'%s.*',mountpoint='/',fstype=~'ext4|xfs'} ) / node_filesystem_size_bytes {instance=~'%s.*',mountpoint='/',fstype=~'ext4|xfs'})",e.Ip,e.Ip),
				},
				logic: entry.Logic,
				specify: func(s string)float64 {
					res,err := strconv.ParseFloat(s,64)
					if err != nil {
						log.Println(err.Error(),"是否因为带单位了？")
					}
					return res
				}(entry.Value),

			})
		}
		triggerentrys.triggers = entrys
		triggerentrys.logicSlice = e.Operation
		return
	case "OpenFiles":
		e.Title = "系统文件描述符"
		var entrys []triggerEntry
		for _,entry :=range e.Expr {
			entrys = append(entrys,triggerEntry{
				currentStatus: prometheusQuery{
					query: fmt.Sprintf("node_filefd_allocated{instance=~'%s.*'}",e.Ip),
				},
				logic: entry.Logic,
				specify: func(s string)float64 {
					res,err := strconv.ParseFloat(s,64)
					if err != nil {
						log.Println(err.Error(),"是否因为带单位了？")
					}
					return res
				}(entry.Value),

			})
		}
		triggerentrys.triggers = entrys
		triggerentrys.logicSlice = e.Operation
		return
	case "EstabAndTimeWait":
		e.Title = "TCP连接数异常"
		var entrys []triggerEntry
		for _,entry :=range e.Expr {
			entrys = append(entrys,triggerEntry{
				currentStatus: prometheusQuery{
					query: fmt.Sprintf("%s{instance=~'%s.*'}",entry.Key,e.Ip),
				},
				logic: entry.Logic,
				specify: func(s string)float64 {
					res,err := strconv.ParseFloat(s,64)
					if err != nil {
						log.Println(err.Error(),"是否因为带单位了？")
					}
					return res
				}(entry.Value),

			})
		}
		triggerentrys.triggers = entrys
		triggerentrys.logicSlice = e.Operation
		return
	case "DiskReadWriteIops":
		e.Title = "磁盘读写频率异常(1m)"
		var entrys []triggerEntry
		for _,entry :=range e.Expr {
			entrys = append(entrys,triggerEntry{
				currentStatus: prometheusQuery{
					query: fmt.Sprintf("irate(%s{instance=~'%s.*',device=~'sda'}[1m])",entry.Key,e.Ip),
				},
				logic: entry.Logic,
				specify: func(s string)float64 {
					res,err := strconv.ParseFloat(s,64)
					if err != nil {
						log.Println(err.Error(),"是否因为带单位了？")
					}
					return res
				}(entry.Value),

			})
		}
		triggerentrys.triggers = entrys
		triggerentrys.logicSlice = e.Operation
		return
	case "DiskReadWriteRate":
		e.Title = "磁盘读写速度异常(1m)"
		var entrys []triggerEntry
		for _,entry :=range e.Expr {
			entrys = append(entrys,triggerEntry{
				currentStatus: prometheusQuery{
					query: fmt.Sprintf("irate(%s{instance=~'%s.*',device=~'sda'}[1m])",entry.Key,e.Ip),
				},
				logic: entry.Logic,
				specify: func(s string)float64 {
					res,err := strconv.ParseFloat(s,64)
					if err != nil {
						log.Println(err.Error(),"是否因为带单位了？")
					}
					return res*1024				//KB/S
				}(entry.Value),

			})
		}
		triggerentrys.triggers = entrys
		triggerentrys.logicSlice = e.Operation
		return
	case "DiskUsage":
	case "NetworkIORate":
		e.Title = "网络读写速度异常(1m)"
		var entrys []triggerEntry
		for _,entry :=range e.Expr {
			entrys = append(entrys,triggerEntry{
				currentStatus: prometheusQuery{
					query: fmt.Sprintf("irate(%s{instance=~'%s.*',device=~'e.*'}[1m])",entry.Key,e.Ip),
				},
				logic: entry.Logic,
				specify: func(s string)float64 {
					res,err := strconv.ParseFloat(s,64)
					if err != nil {
						log.Println(err.Error(),"是否因为带单位了？")
					}
					return res*1024				//KB/S
				}(entry.Value),

			})
		}
		triggerentrys.triggers = entrys
		triggerentrys.logicSlice = e.Operation
		return
	case "TcpConnectionStatus":
		e.Title = "TCP连接数异常"
		var entrys []triggerEntry
		for _,entry :=range e.Expr {
			entrys = append(entrys,triggerEntry{
				currentStatus: prometheusQuery{
					query: fmt.Sprintf("%s{instance=~'%s.*'}",entry.Key,e.Ip),
				},
				logic: entry.Logic,
				specify: func(s string)float64 {
					res,err := strconv.ParseFloat(s,64)
					if err != nil {
						log.Println(err.Error(),"是否因为带单位了？")
					}
					return res
				}(entry.Value),

			})
		}
		triggerentrys.triggers = entrys
		triggerentrys.logicSlice = e.Operation
		return
	}
	return
}



//func (e MonitorAlertEntry) run()   {
//	fmt.Printf("this entry is %+v\n ",e )
//	switch e.Label {
//	case "EstabAndTimeWait":
//		fmt.Println("this is a guage type")  //只有一个表达式
//		result := monitor.Host(e.Ip).FetchEstabAndTWGauge(e.Expr[0].Key)
//		switch e.Expr[0].Logic {
//		case ">":
//			trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//			if result > trigger {
//					e.Title = "TCP连接状态异常"
//					e.AlertTime  = time.Now().Format("2006/1/2 15:04:05")
//					//fmt.Println("ojbk we will send dingding message")
//					fmt.Printf("begin send dingding message %+v\n" , e)
//					go e.sendDingDing(result)   //默认只发送钉钉信息，而且端是根据接受组来发送，而不是根据告警方式
//					//go e.SendDingDingtest()
//					//e.Status = true
//					if !e.Status{
//						k := e.Ip + "-" +  e.Label
//						e.Status = true
//						go redisHset(k,e)  //设置alert状态，
//					}
//			}else {			//测试恢复报警,失败，TODO：status没有上传到redis,每次读取下来的都是false
//				fmt.Println("报警状态： " , e.Status)
//				if e.Status {
//					fmt.Println("begin recover 报警")
//					e.Title = "TCP连接状态恢复"
//					e.AlertTime  = time.Now().Format("2006/1/2 15:04:05")
//					//fmt.Println("ojbk we will send dingding message")
//					fmt.Printf("begin send dingding message %+v\n" , e)
//					go e.sendDingDing(result)   //默认只发送钉钉信息，而且端是根据接受组来发送，而不是根据告警方式
//					k := e.Ip + "-" +  e.Label
//					e.Status = false
//					go redisHset(k,e)  //设置alert状态，
//
//				}
//				//e.Status = false
//			}
//		case ">=":
//			trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//			if result >= trigger {
//				fmt.Println("ojbk we will send dingding message")
//			}
//		case "<":
//			trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//			if result < trigger {
//				fmt.Println("ojbk we will send dingding message")
//			}
//		case "<=":
//			trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//			if result <= trigger {
//				fmt.Println("ojbk we will send dingding message")
//			}
//		case "!=":
//			trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//			if result != trigger {
//				fmt.Println("ojbk we will send dingding message")
//			}
//		default:
//			fmt.Println("错误的操作符号，无法判断逻辑")
//		}
//
//	case "DiskReadWriteRate":
//		fmt.Println("this is a Histogram type")  //只有一个表达式
//		fmt.Println("test disk read write rate ")
//		l := len(e.Expr)
//		b := []bool{}
//		r := []float64{}
//
//		for _,m := range e.Expr {
//			result := monitor.Host(e.Ip).FetchDiskRWRateHistogram(m.Key)
//			r = append(r,result)
//			switch m.Logic {
//			case ">":
//				trigger,_ := strconv.ParseFloat(m.Value,64)
//				fmt.Println("debug herer " , e, "and result is " , result ,"and triger is" , trigger)
//				if result > trigger {
//					e.Title = "磁盘读取速率异常"
//					e.AlertTime  = time.Now().Format("2006/1/2 15:04:05")
//					b = append(b,true)
//				}else {
//					b = append(b,false)
//				}
//			case ">=":
//				trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//				if result >= trigger {
//					fmt.Println("ojbk we will send dingding message")
//				}
//			case "<":
//				trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//				if result < trigger {
//					fmt.Println("ojbk we will send dingding message")
//				}
//			case "<=":
//				trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//				if result <= trigger {
//					fmt.Println("ojbk we will send dingding message")
//				}
//			case "!=":
//				trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//				if result != trigger {
//					fmt.Println("ojbk we will send dingding message")
//				}
//			default:
//				fmt.Println("错误的操作符号，无法判断逻辑")
//			}
//		}
//		if l > 1 {  //多组指标
//			if  e.Operation == "AND" {
//				if b[0] && b[1] {
//					go e.sendDingDing(r[0]) //默认先发第一个指标值
//				}
//			} else if  e.Operation == "OR" {
//				if b[0] || b[1] {
//					go e.sendDingDing(r[0]) //需要send.go 调整
//				}
//			}
//		} else {
//				if b[0] {
//					go e.sendDingDing(r[0])
//				}
//		}
//
//	case "DiskReadWriteIops":
//		fmt.Println("this is a Histogram type")  //只有一个表达式
//		fmt.Println("test disk read write rate ")
//		l := len(e.Expr)
//		b := []bool{}
//		r := []float64{}
//
//		for _,m := range e.Expr {
//			result := monitor.Host(e.Ip).FetchDiskRWIOPSHistogram(m.Key)
//			r = append(r,result)
//			switch m.Logic {
//			case ">":
//				trigger,_ := strconv.ParseFloat(m.Value,64)
//				fmt.Println("debug herer " , e, "and result is " , result ,"and triger is" , trigger)
//				if result > trigger {
//					e.Title = "磁盘读取速率异常"
//					e.AlertTime  = time.Now().Format("2006/1/2 15:04:05")
//					b = append(b,true)
//				}else {
//					b = append(b,false)
//				}
//			case ">=":
//				trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//				if result >= trigger {
//					fmt.Println("ojbk we will send dingding message")
//				}
//			case "<":
//				trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//				if result < trigger {
//					fmt.Println("ojbk we will send dingding message")
//				}
//			case "<=":
//				trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//				if result <= trigger {
//					fmt.Println("ojbk we will send dingding message")
//				}
//			case "!=":
//				trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//				if result != trigger {
//					fmt.Println("ojbk we will send dingding message")
//				}
//			default:
//				fmt.Println("错误的操作符号，无法判断逻辑")
//			}
//		}
//		if l > 1 {  //多组指标
//			if  e.Operation == "AND" {
//				if b[0] && b[1] {
//					go e.sendDingDing(r[0]) //默认先发第一个指标值
//				}
//			} else if  e.Operation == "OR" {
//				if b[0] || b[1] {
//					go e.sendDingDing(r[0]) //需要send.go 调整
//				}
//			}
//		} else {
//			if b[0] {
//				go e.sendDingDing(r[0])
//			}
//		}
//
//	case "NetworkIORate":
//		fmt.Println("this is a Histogram type")  //只有一个表达式
//		fmt.Println("test disk read write rate ")
//		l := len(e.Expr)
//		b := []bool{}
//		r := []float64{}
//
//		for _,m := range e.Expr {
//			result := monitor.Host(e.Ip).FetchNetworkIOHistogram(m.Key)
//			r = append(r,result)
//			switch m.Logic {
//			case ">":
//				trigger,_ := strconv.ParseFloat(m.Value,64)
//				fmt.Println("debug herer " , e, "and result is " , result ,"and triger is" , trigger)
//				if result > trigger {
//					e.Title = "网络读取速率异常"
//					e.AlertTime  = time.Now().Format("2006/1/2 15:04:05")
//					b = append(b,true)
//				}else {
//					b = append(b,false)
//				}
//			case ">=":
//				trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//				if result >= trigger {
//					fmt.Println("ojbk we will send dingding message")
//				}
//			case "<":
//				trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//				if result < trigger {
//					fmt.Println("ojbk we will send dingding message")
//				}
//			case "<=":
//				trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//				if result <= trigger {
//					fmt.Println("ojbk we will send dingding message")
//				}
//			case "!=":
//				trigger,_ := strconv.ParseFloat(e.Expr[0].Value,64)
//				if result != trigger {
//					fmt.Println("ojbk we will send dingding message")
//				}
//			default:
//				fmt.Println("错误的操作符号，无法判断逻辑")
//			}
//		}
//		if l > 1 {  //多组指标
//			if  e.Operation == "AND" {
//				if b[0] && b[1] {
//					go e.sendDingDing(r[0]) //默认先发第一个指标值
//				}
//			} else if  e.Operation == "OR" {
//				if b[0] || b[1] {
//					go e.sendDingDing(r[0]) //需要send.go 调整
//				}
//			}
//		} else {
//			if b[0] {
//				go e.sendDingDing(r[0])
//			}
//		}
//	}
//
//}


func redisHget()  (alertEntries []string){
	redisconn := db.RedisPool.Get()
	defer redisconn.Close()
	//遍历所有的告警值
	triggerKey,_  :=  conf.Cfg.GetValue("redis","trigger_key")
	alertEntries, err := redis.Strings(redisconn.Do("HVALS", triggerKey))
	if err != nil {
		fmt.Println("redis HGET error:", err)
	} else {
		//fmt.Println("The all altermanager entry is" , alertEntries)
	}
	return
}